package com.weifly.weistock.module.stockmonitor.impl;

import com.weifly.weistock.bo.DiffRateBO;
import com.weifly.weistock.core.market.bo.StockDayBO;
import com.weifly.weistock.core.util.CalculateUtils;
import com.weifly.weistock.core.util.WeistockUtils;
import com.weifly.weistock.module.stockdata.StockDataConverter;
import com.weifly.weistock.module.stockmonitor.bo.StockMonitorDayBO;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * 股票监控配置计算器
 *
 * @author weifly
 * @since 2021/6/30
 */
public class StockMonitorConfigCalculator {

    private int recentDayNumber = 60; // 最近60天数据
    private List<StockDayBO> dayList; // 原始数据集合
    private TreeMap<String, StockMonitorDayBO> stockMonitorDayMap = new TreeMap<>(); // 股票监控天Map
    private StockDayBO currentDay = null; // 当天
    private StockDayBO nextDay = null; // 下一天
    private LinkedList<StockDayBO> recentDayList = new LinkedList<>(); // 最近天集合

    public StockMonitorConfigCalculator(List<StockDayBO> dayList) {
        this.dayList = dayList;
    }

    public Map<String, StockMonitorDayBO> calc() {
        ListIterator<StockDayBO> iter = this.dayList.listIterator(this.dayList.size());
        while (iter.hasPrevious()) {
            StockDayBO stockDay = iter.previous();
            this.createStockMonitorDay(stockDay);
            // 计算diff、rate
            this.calcDiffRate(stockDay);
            // 计算up、down
            this.calcUpAndDown(stockDay);
        }
        // 计算all百分位
        this.calcAllPercent();
        return this.stockMonitorDayMap;
    }

    private void createStockMonitorDay(StockDayBO stockDay) {
        StockMonitorDayBO stockMonitorDay = new StockMonitorDayBO();
        stockMonitorDay.setDay(stockDay.getDay());
        stockMonitorDay.setClose(stockDay.getClose());
        stockMonitorDayMap.put(stockDay.getDay(), stockMonitorDay);
    }

    private void calcDiffRate(StockDayBO stockDay) {
        this.nextDay = this.currentDay;
        this.currentDay = stockDay;
        if (this.nextDay == null || this.currentDay == null) {
            return;
        }

        StockMonitorDayBO monitorDay = this.stockMonitorDayMap.get(this.nextDay.getDay());
        double diff = WeistockUtils.subtract(this.nextDay.getClose(), this.currentDay.getClose());
        double rate = new BigDecimal(WeistockUtils.multi(diff, 100)).divide(new BigDecimal(this.currentDay.getClose()), 2, RoundingMode.HALF_UP).doubleValue();
        monitorDay.setDiff(diff);
        monitorDay.setRate(rate);
    }

    private void calcUpAndDown(StockDayBO stockDay) {
        this.recentDayList.addLast(stockDay);
        while (this.recentDayList.size() > this.recentDayNumber) {
            this.recentDayList.removeFirst();
        }
        if (this.recentDayList.size() < this.recentDayNumber) {
            // 小于指定天数，不计算
            return;
        }
        StockDayBO targetDay = this.recentDayList.getFirst();
        StockMonitorDayBO targetMonitorDay = this.stockMonitorDayMap.get(targetDay.getDay());

        // 计算up
        String up_lowest_day = null;
        double up_lowest_price = Double.MAX_VALUE;
        for (StockDayBO oneDay : this.recentDayList) {
            double lowPrice = StockDataConverter.getLow(oneDay);
            if (lowPrice > 0 && up_lowest_price > lowPrice) {
                up_lowest_day = oneDay.getDay();
                up_lowest_price = lowPrice;
            }
        }
        DiffRateBO upDiffRate = CalculateUtils.calcDiffAndRate(up_lowest_price, targetDay.getClose());
        targetMonitorDay.setUp_lowest_day(up_lowest_day);
        targetMonitorDay.setUp_lowest_price(up_lowest_price);
        targetMonitorDay.setUp_diff(upDiffRate.getDiff());
        targetMonitorDay.setUp_rate(upDiffRate.getRate());

        // 计算down
        String down_highest_day = null;
        double down_highest_price = Double.MIN_VALUE;
        for (StockDayBO oneDay : this.recentDayList) {
            if (down_highest_price < oneDay.getHigh()) {
                down_highest_day = oneDay.getDay();
                down_highest_price = oneDay.getHigh();
            }
        }
        DiffRateBO downDiffRate = CalculateUtils.calcDiffAndRate(down_highest_price, targetDay.getClose());
        targetMonitorDay.setDown_highest_day(down_highest_day);
        targetMonitorDay.setDown_highest_price(down_highest_price);
        targetMonitorDay.setDown_diff(downDiffRate.getDiff());
        targetMonitorDay.setDown_rate(downDiffRate.getRate());
    }

    private void calcAllPercent() {
        int upAllCount = 0; // 有上涨幅度的天数
        Map<String, Integer> upSmallMap = new HashMap<>();
        int downAllCount = 0; // 有下跌幅度的天数
        Map<String, Integer> downBigMap = new HashMap<>();
        for (StockDayBO targetDay : this.dayList) {
            StockMonitorDayBO targetMonitorDay = this.stockMonitorDayMap.get(targetDay.getDay());
            if (StringUtils.isNotBlank(targetMonitorDay.getUp_lowest_day())) {
                upAllCount++;
                int smallDayNumber = 0;
                for (StockMonitorDayBO oneMonitor : this.stockMonitorDayMap.values()) {
                    // oneMonitor.getUp_lowest_day() 不为空时，才是有效的天
                    if (StringUtils.isNotBlank(oneMonitor.getUp_lowest_day()) && (oneMonitor.getUp_rate() < targetMonitorDay.getUp_rate())) {
                        smallDayNumber++;
                    }
                }
                upSmallMap.put(targetDay.getDay(), smallDayNumber);
            }
            if (StringUtils.isNotBlank(targetMonitorDay.getDown_highest_day())) {
                downAllCount++;
                int bigDayNumber = 0;
                for (StockMonitorDayBO oneMonitor : this.stockMonitorDayMap.values()) {
                    // oneMonitor.getDown_highest_day() 不为空时，才是有效的天
                    if (StringUtils.isNotBlank(oneMonitor.getDown_highest_day()) && (oneMonitor.getDown_rate() > targetMonitorDay.getDown_rate())) {
                        bigDayNumber++;
                    }
                }
                downBigMap.put(targetDay.getDay(), bigDayNumber);
            }
        }

        for (Map.Entry<String, Integer> entry : upSmallMap.entrySet()) {
            double percent = CalculateUtils.computePercent(entry.getValue() + 1, upAllCount);
            this.stockMonitorDayMap.get(entry.getKey()).setUp_rate_all_percent(percent);
        }
        for (Map.Entry<String, Integer> entry : downBigMap.entrySet()) {
            double percent = CalculateUtils.computePercent(entry.getValue() + 1, downAllCount);
            this.stockMonitorDayMap.get(entry.getKey()).setDown_rate_all_percent(percent);
        }
        // 上涨百分比 - 下跌百分比
        for (StockMonitorDayBO monitorDayBO : this.stockMonitorDayMap.values()) {
            if (StringUtils.isNotEmpty(monitorDayBO.getUp_lowest_day())) {
                double upPercent = monitorDayBO.getUp_rate_all_percent();
                double downPercent = monitorDayBO.getDown_rate_all_percent();
                monitorDayBO.setDiff_rate_percent(WeistockUtils.subtract(upPercent, downPercent));
            }
        }
    }
}
